package com.gitee.gateway.core.utils;

import com.noenv.jsonpath.JsonPath;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.apache.commons.lang3.RandomStringUtils;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.Checksum;

/**
 * 常用的字符串工具
 * !!!IMPORTANT!!!! 请勿修改 token 生成算法
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils {

    private final static int DEFAULT_CRC_LENGTH = 4; //default length of crc
    private final static String CRC_SALT = JsonPath.getString(Configuration.load(), "$.global.tokenSalt"); //加盐后再进行 CRC
    private final static String EMAIL_REGEX = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
    private final static String MOBILE_REGEX = "^((13[0-9])|(14[0|5|6|7|9])|(15[0-3])|(15[5-9])|(16[6|7])|(17[2|3|5|6|7|8])|(18[0-9])|(19[1|8|9]))\\d{8}$";

    public static boolean isEmail(String email) {
        return email != null && Pattern.matches(EMAIL_REGEX, email);
    }

    public static boolean isMobile(String mobile) {
        return mobile != null && Pattern.matches(MOBILE_REGEX, mobile);
    }

    public static boolean isPasswordValid(String pwd, int minLen) {
        Pattern patternPwd = Pattern.compile("(?=.{" + minLen + ",})" +     // "" followed by 9+ symbols
                "(?=.*[a-z])" +     // --- ' ' --- at least 1 lower
                "(?=.*[A-Z])" +     // --- ' ' --- at least 1 upper
                "(?=.*[0-9])" +     // --- ' ' --- at least 1 digit
                //"(?=.*\\p{Punct})"+ // --- ' ' --- at least 1 symbol
                ".*");              // the actual characters
        return patternPwd.matcher(pwd).matches();
    }

    public static String replaceInPos(String str, int from, int to, char replacement) {
        if (str == null || str.isEmpty())
            return str;
        if (from < 0)
            from = 0;
        if (to > str.length())
            to = str.length();
        if (from >= to)
            return str;
        char[] chars = str.toCharArray();
        Arrays.fill(chars, from, to, replacement);
        return new String(chars);
    }

    public static List<String> lines(String str) {
        return str == null ? Collections.emptyList() : Arrays.asList(str.split("[\\r\\n]+"));
    }

    public static boolean isValidURL(String url) {
        if (StringUtils.isBlank(url))
            return false;
        return url.matches("^(https?)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
    }

    /**
     * 生成一个新的 token
     * token 使用 crc 自校验，将四位校验码均匀插入到随机码中
     *
     * @param length 令牌长度
     * @return 令牌
     */
    public static String newToken(int length) {
        return newToken("", length, DEFAULT_CRC_LENGTH);
    }

    public static String newToken(String prefix, int length) {
        return newToken(prefix, length, DEFAULT_CRC_LENGTH);
    }

    public static String newToken(String prefix, int length, int crcLength) {
        String rand = prefix + RandomStringUtils.randomAlphanumeric(length - crcLength - prefix.length()).toLowerCase();
        return rand + crc((CRC_SALT + rand).getBytes(), crcLength);
    }

    /**
     * 自校验 token 是否有效
     *
     * @param token 令牌
     * @return 是否有效
     */
    public static boolean isValidToken(String token) {
        return isValidToken(token, DEFAULT_CRC_LENGTH);
    }

    /**
     * 自校验 token 是否有效
     *
     * @param token     令牌
     * @param crcLength 校验码长度
     * @return 是否有效
     */
    public static boolean isValidToken(String token, int crcLength) {
        if (token == null || token.length() <= crcLength)
            return false;
        String rand = token.substring(0, token.length() - crcLength);
        String crc = crc((CRC_SALT + rand).getBytes(), crcLength);
        String crcOld = token.substring(token.length() - crcLength);
        //System.out.printf("token:%s,rand:%s,crc:%s,old=%s\n", token, rand, crc, crcOld);
        return crc.equals(crcOld);
    }

    private static String crc(byte[] bytes, int maxLength) {
        Checksum crc32 = new CRC32();
        crc32.update(bytes);
        String crc = Long.toString(crc32.getValue(), 36);
        return StringUtils.left(crc, maxLength);
    }

    /**
     * 判断两个 json 是否一致
     *
     * @param obj1 json1
     * @param obj2 json2
     * @return 是否一致
     */
    public static boolean jsonEquals(JsonObject obj1, JsonObject obj2) {
        if (obj1 == null && obj2 == null)
            return true;
        if (obj1 == null)
            return false;
        if (obj2 == null)
            return false;
        return obj1.encode().equals(obj2.encode());
    }

    public static boolean jsonEquals(JsonArray obj1, JsonArray obj2) {
        if (obj1 == null && obj2 == null)
            return true;
        if (obj1 == null)
            return false;
        if (obj2 == null)
            return false;
        return obj1.encode().equals(obj2.encode());
    }

    public static void main(String[] args) {
        //System.out.println(Integer.toString(35, 36));
        for (int i = 0; i < 20; i++) {
            String token = newToken(12);
            System.out.println(token + " => " + isValidToken(token));
            token = newToken(20);
            System.out.println(token + " => " + isValidToken(token));
        }
    }

}
